package com.zwh.se.collection.concurrent.blocking_queue;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @author yexi
 * DelayQueue:一个无界的BlockingQueue，用于放置实现了Delayed接口的对象，其中的对象只能在其到期时才能从队列中取走。
 * 当生产者线程调用put之类的方法加入元素时，会触发Delayed接口中的compareTo方法进行排序，也就是说队列中元素的顺序是按到期时间排序的，而非它们进入队列的顺序。
 * 排在队列头部的元素是最早到期的，越往后到期时间赿晚。
 * 消费者线程查看队列头部的元素，注意是查看不是取出。然后调用元素的getDelay方法，如果此方法返回的值小于0或者等于0，则消费者线程会从队列中取出此元素，并进行处理。
 * 如果getDelay方法返回的值大于0，则消费者线程wait返回的时间值后，再从队列头部取出元素，此时元素应该已经到期。
 *
 * 实际场景：
 * 1、 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。
 * 2、 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。
 * 3、 关闭空闲连接。服务器中，有很多客户端的连接，空闲一段时间之后需要关闭之。
 * 4、 缓存。缓存中的对象，超过了空闲时间，需要从缓存中移出。
 * 5、 任务超时处理。在网络协议滑动窗口请求应答式交互时，处理超时未响应的请求等。
 */
public class DelayQueueDemo {

    static class User implements Delayed {
        private int id;//用户id
        private String name;//用户名字
        private long endTime;//结束时间

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public long getEndTime() {
            return endTime;
        }

        public void setEndTime(long endTime) {
            this.endTime = endTime;
        }

        public User(int id, String name, long endTime) {
            this.id = id;
            this.name = name;
            this.endTime = endTime;
        }

        //只包装用户名字就可以
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }

        //看剩余时间
        @Override
        public long getDelay(TimeUnit unit) {
            //计算剩余时间 剩余时间小于0 <=0  证明已经到期
            return this.getEndTime() - System.currentTimeMillis();
        }

        //看谁先被移除
        @Override
        public int compareTo(Delayed o) {
            //队列中数据 到期时间的比较
            User other = (User) o;
            return ((Long) (this.getEndTime())).compareTo((Long) (other.getEndTime()));
        }
    }

    //创建一个队列：
    DelayQueue<User> dq = new DelayQueue<>();

    //登录游戏：
    public void login(User user) {
        dq.add(user);
        System.out.println("用户：[" + user.getId() + "],[" + user.getName() + "]已经登录，预计下机时间为：" + user.getEndTime());
    }

    //时间到，退出游戏，队列中移除：
    public void logout() {
        //打印队列中剩余的人：
        System.out.println(dq);
        try {
            User user = dq.take();
            System.out.println("用户：[" + user.getId() + "],[" + user.getName() + "]上机时间到，自动退出游戏");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //获取在线人数：
    public int onlineSize() {
        return dq.size();
    }

    public static void main(String[] args) {
        DelayQueueDemo test = new DelayQueueDemo();
        //添加登录的用户：
        test.login(new User(1, "张三", System.currentTimeMillis() + 5000));
        test.login(new User(2, "李四", System.currentTimeMillis() + 2000));
        test.login(new User(3, "王五", System.currentTimeMillis() + 10000));
        //一直监控
        while (true) {
            //到期的话，就自动下线：
            test.logout();
            //队列中元素都被移除了的话，那么停止监控，停止程序即可
            if (test.onlineSize() == 0) {
                break;
            }
        }

    }
}
